// modbus_base.cpp
#include "modbus_base.h"
#include <algorithm>
#include <iostream>

ModbusBase::ModbusBase() {
}

ModbusBase::~ModbusBase() {
}


// 帧长度预测函数
size_t ModbusBase::predict_frame_length(const uint8_t* data, size_t len, uint8_t machine_addr) const {
    // 最小帧长度检查
    if (len < 4) return 0; // 地址(1) + 功能码(1) + CRC(2)
    const uint8_t function_code = data[1];
    
    // 错误响应帧处理
    if (function_code & 0x80) {
        return len >= 5 ? 5 : 0; // 地址(1) + 异常码(1) + 错误码(1) + CRC(2)
    }
    switch (static_cast<FunctionCode>(function_code)) {
        case READ_HOLDING_REGISTERS:
            if (len < 3) return 0; // 需要字节计数
            // 3 = 地址(1) + 功能码(1) + 字节计数(1)
            return 3 + static_cast<size_t>(data[2]) + 2; // + 数据 + CRC
        
        case WRITE_SINGLE_REGISTER:
            return 8; // 固定长度
        
        case WRITE_MULTIPLE_REGISTERS:
            return 8; // 固定长度
        
        default:
            // 无法预判的帧，尝试最大长度保护
            return len < 256 ? 0 : 256; // 防止无限等待
    }
}


bool ModbusBase::validate_frame_crc(const uint8_t* data, size_t length) const {
    if (length < 3) return false; // 至少3字节才有CRC
    
    uint16_t received_crc = (data[length-1] << 8) | data[length-2];
    uint16_t calculated_crc = calculate_crc(data, length - 2);
    return received_crc == calculated_crc;
}


std::vector<uint8_t> ModbusBase::create_request(FunctionCode function_code, 
                                              uint8_t machine_addr,
                                              uint16_t start_addr, 
                                              uint16_t reg_count,
                                              const std::vector<uint16_t>& data) const {
    std::vector<uint8_t> request;
    request.reserve(256); // 预分配足够空间

    // 设备地址和功能码
    request.push_back(machine_addr);
    request.push_back(static_cast<uint8_t>(function_code));

    switch(function_code) {
        case READ_HOLDING_REGISTERS:
            // std::cout<<"READ_HOLDING_REGISTERS"<<std::endl;
            // 读寄存器请求
            request.push_back(static_cast<uint8_t>(start_addr >> 8));
            request.push_back(static_cast<uint8_t>(start_addr & 0xFF));
            request.push_back(static_cast<uint8_t>(reg_count >> 8));
            request.push_back(static_cast<uint8_t>(reg_count & 0xFF));
            break;
            
        case WRITE_SINGLE_REGISTER:
            // std::cout<<"WRITE_SINGLE_REGISTER"<<std::endl;
            // 写单个寄存器请求
            request.push_back(static_cast<uint8_t>(start_addr >> 8));
            request.push_back(static_cast<uint8_t>(start_addr & 0xFF));
            request.push_back(static_cast<uint8_t>(data[0] >> 8));
            request.push_back(static_cast<uint8_t>(data[0] & 0xFF));
            break;
            
        case WRITE_MULTIPLE_REGISTERS:
            // std::cout<<"WRITE_MULTIPLE_REGISTERS"<<std::endl;
            // 写多个寄存器请求
            request.push_back(static_cast<uint8_t>(start_addr >> 8));
            request.push_back(static_cast<uint8_t>(start_addr & 0xFF));
            request.push_back(static_cast<uint8_t>(reg_count >> 8));
            request.push_back(static_cast<uint8_t>(reg_count & 0xFF));
            request.push_back(static_cast<uint8_t>(reg_count * 2)); // 字节数
            
            for (const auto& value : data) {
                request.push_back(static_cast<uint8_t>(value >> 8));
                request.push_back(static_cast<uint8_t>(value & 0xFF));
            }
            break;
    }
    
    // 计算CRC并添加到请求末尾
    uint16_t crc = calculate_crc(request.data(), request.size());
    request.push_back(static_cast<uint8_t>(crc & 0xFF));
    request.push_back(static_cast<uint8_t>(crc >> 8));
    
    return request;
}

// 标准的ModBus_rtu的CRC校验算法
uint16_t ModbusBase::calculate_crc(const uint8_t* buffer, uint16_t length) const {
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < length; i++) {
        crc ^= buffer[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc >>= 1;
                crc ^= 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}
